package com.gitee.wsl.jvm.task.thread

import timber.log.Timber
import java.util.concurrent.Callable

private class Retry {
    fun <T> doRetry(
        retryTimes: Int = 2,
        sleepTimeInMilliSecond: Long = RetryUtil.MAX_SLEEP_MILLISECOND,
        exponential: Boolean = false,
        retryExceptionClazz: List<Class<*>>? = null,
        callable: Callable<T>
    ): T {
        require(retryTimes >= 1) {
            String.format(
                "系统编程错误, 入参retryTime[%d]不能小于1 !",
                retryTimes
            )
        }
        var saveException: Exception? = null
        for (i in 0 until retryTimes) {
            try {
                return call(callable)
            } catch (e: Exception) {
                saveException = e
                if (i == 0) {
                    Timber.e(java.lang.String.format("Exception when calling callable, 异常Msg:%s", saveException.message), saveException)
                }
                if (!retryExceptionClazz.isNullOrEmpty()) {
                    var needRetry = false
                    for (eachExceptionClass in retryExceptionClazz) {
                        if (eachExceptionClass == e.javaClass) {
                            needRetry = true
                            break
                        }
                    }
                    if (!needRetry) {
                        throw RuntimeException(saveException)
                    }
                }
                if (i + 1 < retryTimes && sleepTimeInMilliSecond > 0) {
                    val startTime = System.currentTimeMillis()
                    var timeToSleep: Long
                    timeToSleep = if (exponential) {
                        sleepTimeInMilliSecond * Math.pow(2.0, i.toDouble()).toLong()
                    } else {
                        sleepTimeInMilliSecond
                    }
                    if (timeToSleep >= RetryUtil.MAX_SLEEP_MILLISECOND) {
                        timeToSleep = RetryUtil.MAX_SLEEP_MILLISECOND
                    }
                    try {
                        Thread.sleep(timeToSleep)
                    } catch (ignored: InterruptedException) {
                    }
                    val realTimeSleep = System.currentTimeMillis() - startTime
                    Timber.e(
                        java.lang.String.format(
                            "Exception when calling callable, 即将尝试执行第%s次重试.本次重试计划等待[%s]ms,实际等待[%s]ms, 异常Msg:[%s]",
                            i + 1,
                            timeToSleep,
                            realTimeSleep,
                            e.message
                        )
                    )
                }
            }
        }
        throw RuntimeException(saveException)
    }

    @Throws(Exception::class)
    protected fun <T> call(callable: Callable<T>): T {
        return callable.call()
    }
}

object RetryUtil {
    const val MAX_SLEEP_MILLISECOND = 256 * 1000L

    /**
     * 重试次数工具方法.
     *
     * @param callable 实际逻辑
     * @param retryTimes 最大重试次数（>1）
     * @param sleepTimeInMilliSecond 运行失败后休眠对应时间再重试
     * @param exponential 休眠时间是否指数递增
     * @param <T> 返回值类型
     * @return 经过重试的callable的执行结果
    </T> */
    fun <T> executeWithRetry(
        callable: Callable<T>,
        retryTimes: Int,
        sleepTimeInMilliSecond: Long,
        exponential: Boolean
    ): T {
        val retry = Retry()
        return retry.doRetry( retryTimes, sleepTimeInMilliSecond, exponential, null,callable)
    }
}